package guoan.service;

import guoan.core.common.WebConfig;
import guoan.core.dto.DataGridModel;
import guoan.core.dto.PageResponse;
import guoan.core.dto.RestResponse;
import guoan.domain.Exam;
import guoan.domain.ExamItem;
import guoan.domain.ExamItemStatus;
import guoan.domain.RandomKey;
import guoan.domain.Student;
import guoan.repository.ExamItemRepository;
import guoan.utils.CommonUtils;

import java.util.List;
import java.util.Map;

import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.google.common.base.Preconditions;
import com.google.common.collect.Maps;

@Service
@Transactional
public class ExamItemService extends PageSerivce<ExamItem> {
	protected final Logger log = LoggerFactory.getLogger(getClass());

	@Autowired
	private ExamItemRepository examItemRepository;

	@Autowired
	private ExamService examService;
	@Autowired
	private StudentService studentService;
	@Autowired
	private ExamItemStatusService examItemStatusService;
	@Autowired
	private RandomKeyService randomKeyService;

	/**
	 * joinExam:预约报名<br/>
	 * 
	 * @author he.sun
	 * @param student
	 * @param examId
	 * @param openHours
	 * @return RestResponse<Boolean>
	 */
	@Transactional(readOnly = false, isolation = Isolation.SERIALIZABLE, timeout = 10)
	public RestResponse<Boolean> joinExamItem(Student student, Long examId, Long openHours, String randomKey,
			String carType) {
		log.debug("[预约报名]开始：studentId:{},examId:{},openHours:{}", student, examId, openHours);
		// 获得可用的预约模拟 在开始报名时间之后
		Exam exam = examService.findByIdAndResTimeLessThan(examId);
		Preconditions.checkNotNull(exam, "抱歉，预约模拟报名还未开始！");

		// 判断报名时间是否在开放时间之前
		ExamItemStatus examItemStatus = examItemStatusService.findByExamIdAndOpenHours(examId, openHours);
		Preconditions.checkNotNull(exam, "抱歉，您选择的开放时间已不可用");
		DateTime now = new DateTime();
		int h = now.getHourOfDay();
		int day = now.getDayOfYear();
		if (day == exam.getExamTime().getDayOfYear() && h > examItemStatus.getOpenHours().intValue()) {
			return RestResponse.error("当前模拟已经开始无法预约");
		}

		Preconditions.checkNotNull(exam, "抱歉，预约模拟报名还未开始或已经结束！");
		// 还未绑定的学员，此时需要新增
		Student studentk = studentService.findByStudentOpenid(student.getStudentOpenid());
		if (studentk == null) {
			studentk = studentService.save(student);
		} else {
			studentk.setContactNumberF(student.getContactNumberF());
			studentk.setStudentNo(student.getStudentNo());
			studentk.setStudentName(student.getStudentName());
			studentk = studentService.save(studentk);
		}

		// 查看预约模拟绑定码是否存在并合理
		Preconditions.checkNotNull(randomKey, "抱歉，请填写预约模拟绑定码！");

		// 判断是否可以预约
		// 如果预约模拟时间状态不为 null
		log.debug("是否可以预约：examItemStatus:{}", examItemStatus);
		// 是否已经预约预约过
		Long count = examItemRepository.countByExamIdAndOpenHoursAndStudentOpenid(examId, openHours,
				student.getStudentOpenid());
		if (count.intValue() > 0) {
			return RestResponse.error("抱歉，当前时间段您已经报名过，无法重复报名");
		}
		// 每次预约只可以报名三次
		Long allCount = examItemRepository.countByExamIdAndStudentOpenid(examId, student.getStudentOpenid());
		if (allCount.intValue() > 2) {
			return RestResponse.error("抱歉，同一个模拟预约日期，您最多可以预约报名三次，请选择其他预约模拟时间进行预约");
		}

		// 绑定码是否正确
		RandomKey randomKeye = randomKeyService.findByKey(randomKey);
		if (randomKeye == null) {
			return RestResponse.error("抱歉，绑定码错误！");
		} else {
			if (randomKeye.getIsUse().intValue() == 1 || randomKeye.getCoachType().intValue() != 1) {
				return RestResponse.error("抱歉，绑定码错误！");
			}
		}

		// 获得模拟时间预计预约模拟ID的状态中的当前预约模拟数
		int an = examItemStatusService.updateAppNum(examId, openHours);
		log.debug("是否可以预约,已经报名数：an:{}", an);

		// 如果已经达到最大数则不允许报名
		if (an != 1) {
			log.debug("已经达到最大数");
			return RestResponse.error("抱歉，本时间段预约模拟已经报满，请选择其余时间短报名");
		} else {

			// 报名信息
			ExamItem examItem = new ExamItem();
			examItem.setContactNumberF(student.getContactNumberF());
			examItem.setContactNumberS(student.getContactNumberS());
			examItem.setExamId(exam.getId());
			examItem.setExamTime(exam.getExamTime());
			examItem.setExamTitle(exam.getTitle());
			examItem.setJoinTime(new DateTime());
			examItem.setOpenHours(examItemStatus.getOpenHours());
			// 12位随即号
			examItem.setStudentKey(CommonUtils.randomPassword());
			examItem.setStudentName(student.getStudentName());
			examItem.setStudentOpenid(student.getStudentOpenid());
			examItem.setRandomKey(randomKey);
			examItem.setCarType(carType);

			log.debug("报名信息 ExamItem:{}", examItem);

			// 报名
			ExamItem resExamItem = save(examItem);
			log.debug("报名结果 ExamItem:{}", examItem);
			if (resExamItem != null && resExamItem.getId() != null) {
				log.debug("更新一下报名状态:{}", examItemStatus);

				// 更新绑定码状态
				randomKeye.setStudentId(student.getId());
				randomKeye.setIsUse(1L);
				randomKeyService.save(randomKeye);
				log.debug("[预约报名，成功]结束");
				return RestResponse.ok(true);
			} else {
				log.debug("[预约报名，失败]，报名信息没有正确添加");
				return RestResponse.error("报名信息没有正确添加");
			}
		}
	}

	public ExamItem save(ExamItem examItem) {
		return examItemRepository.save(examItem);
	}

	/**
	 * cancelExam:取消报名. <br/>
	 *
	 * @author he.sun
	 * @param examItemItemId
	 * @return RestResponse<Boolean>
	 */
	public RestResponse<Boolean> cancelExam(Long examItemId) {
		// 获得报名信息
		ExamItem examItem = findOne(examItemId);
		Preconditions.checkNotNull(examItem, "抱歉，报名信息不存在！");

		ExamItemStatus examItemStatus = examItemStatusService.findByExamIdAndOpenHours(examItem.getExamId(),
				examItem.getOpenHours());
		Preconditions.checkNotNull(examItemStatus, "抱歉，报名信息不存在！");

		DateTime now = new DateTime();
		int h = now.getHourOfDay();
		int day = now.getDayOfYear();
		if (day == examItem.getExamTime().getDayOfYear() && h > examItemStatus.getOpenHours().intValue()) {
			return RestResponse.error("当前模拟已经开始无法取消预约预约");
		} else {
			// 取消报名
			delete(examItemId);
			return RestResponse.ok(true);
		}
	}

	/**
	 * delete:删除报名信息. <br/>
	 *
	 * @author he.sun
	 * @param examItemItemId
	 */
	public void delete(Long examItemItemId) {
		// TODO Auto-generated method stub
		ExamItem examItem = findOne(examItemItemId);
		examItemRepository.delete(examItem);
		ExamItemStatus examItemStatus = examItemStatusService.findByExamIdAndOpenHours(examItem.getExamId(),
				examItem.getOpenHours());
		if (examItemStatus != null) {
			examItemStatus.setApplicantsNumber(examItemStatus.getApplicantsNumber() - 1);
			examItemStatusService.saveOrUpdate(examItemStatus);
		}
	}

	/**
	 * findOne:查找报名信息. <br/>
	 *
	 * @author he.sun
	 * @param examItemItemId
	 * @return ExamItem
	 */
	private ExamItem findOne(Long examItemItemId) {
		// TODO Auto-generated method stub
		return examItemRepository.findOne(examItemItemId);
	}

	public boolean exists(Long examId) {
		// TODO Auto-generated method stub
		return examItemRepository.exists(examId);
	}

	public PageResponse<ExamItem> page(Long examId, Long openHours, DataGridModel dataGridModel) {
		// TODO Auto-generated method stub
		Map<String, Object> paramMap = Maps.newHashMap();
		paramMap.put("EQ_examId", Preconditions.checkNotNull(examId, "模拟信息不正确"));
		if (openHours != null) {// <- openHours =！null 全部的开放时间
			paramMap.put("EQ_openHours", openHours);
		}
		Page<ExamItem> page = examItemRepository.findAll(buildSpecification(paramMap), buildPageRequest(dataGridModel));
		return formatPageToPageResponse(page, dataGridModel.getDraw());
	}

	public List<ExamItem> findByStudentOpenid(String openid) {
		// TODO Auto-generated method stub
		return examItemRepository.findByStudentOpenid(openid);
	}

	public void deleteByExamId(Long id) {
		// TODO Auto-generated method stub
		examItemRepository.deleteByExamId(id);
	}

	public Long countByExamId(Long id) {
		// TODO Auto-generated method stub
		return examItemRepository.countByExamId(id);
	}

	public void removeLast() {
		// TODO Auto-generated method stub
		examItemRepository.removeLast();
	}
}
